<template>
  <div class="flex items-center flex-col text-center p-12 h-screen">
    <div class="relative h-full mb-4">
      <video ref="localVideo" class="w-96 h-full bg-gray-200 mb-4 object-cover"></video>
      <video ref="remoteVideo" class="w-32 h-48 absolute bottom-0 right-0 object-cover"></video>
      <div v-if="caller && calling" class="absolute top-2/3 left-36 flex flex-col items-center">
        <p class="mb-4 text-white">等待对方接听...</p>
        <img @click="hangUp" src="/refuse.svg" class="w-16 cursor-pointer" alt="" />
      </div>
      <div v-if="called && calling" class="absolute top-2/3 left-32 flex flex-col items-center">
        <p class="mb-4 text-white">收到视频邀请...</p>
        <div class="flex">
          <img @click="hangUp" src="/refuse.svg" class="w-16 cursor-pointer mr-4" alt="" />
          <img @click="acceptCall" src="/accept.svg" class="w-16 cursor-pointer" alt="" />
        </div>
      </div>
    </div>
    <div class="flex gap-2 mb-4">
      <button
        class="rounded-md bg-indigo-600 px-4 py-2 text-sm font-semibold text-white"
        @click="callRemote"
      >
        发起视频
      </button>
      <button
        class="rounded-md bg-red-600 px-4 py-2 text-sm font-semibold text-white"
        @click="hangUp"
      >
        挂断视频
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, onMounted } from 'vue'
  import { io, Socket } from 'socket.io-client'

  const roomId = '001'
  const called = ref<boolean>(false) // 是否是接收方
  const caller = ref<boolean>(false) // 是否是发起方
  const calling = ref<boolean>(false) // 是否呼叫中
  const communicating = ref<boolean>(false) // 是否视频通话中
  const localVideo = ref<HTMLVideoElement>() // video标签实例，播放本人的视频
  const localStream = ref<any>() // 本地音视频流
  const remoteVideo = ref<HTMLVideoElement>() // video标签实例，播放对方的视频
  const socket = ref<Socket>()
  const peer = ref<any>()

  /*********** 生命周期 **************/
  onMounted(() => {
    const sock = io('http://localhost:3000')
    sock.on('connectionSuccess', () => {
      console.log('连接成功...')
      // 向服务器发送一个加入房间的事件
      sock.emit('joinRoom', roomId)
    })

    sock.on('callRemote', () => {
      // 不是用户A(发起方)
      if (!caller.value) {
        called.value = true // 接收方
        calling.value = true
      }
    })

    // 监听信令服务器广播的同意视频请求
    sock.on('acceptCall', async () => {
      if (caller.value) {
        // 用户A（发送者）收到用户B同意视频的请求
        // 用户A创建RTCPeerConnection，添加本地音视频流，生成offer,并且通关信令服务器将offer发给用户B
        peer.value = new RTCPeerConnection()
        // 添加本地音视频流
        peer.value.addStream(localStream.value)

        // 通过监听onicecandidate事件获取candidate信息(网络信息)
        peer.value.onicecandidate = (event: any) => {
          console.log('用户A的candidate信息', event.candidate)
          if (event.candidate) {
            sock.emit('sendCandidate', { candidate: event.candidate, roomId })
          }
        }

        // 监听onaddstream事件来获取对方的音视频流
        peer.value.onaddstream = (event: any) => {
          console.log('用户A收到用户B的stream', event.stream)
          communicating.value = true
          calling.value = false
          remoteVideo.value!.srcObject = event.stream
          remoteVideo.value!.play()
        }

        // 生成offer
        const offer = await peer.value.createOffer({
          offerToReceiveAudio: true,
          offerToReceiveVideo: true,
        })
        console.log(offer, 82)
        // 在本地设置offer信息
        await peer.value.setLocalDescription(offer)
        // 发送offer
        sock.emit('sendOffer', { offer, roomId })
      }
    })

    // 用户B收到offer
    sock.on('sendOffer', async (offer) => {
      if (called.value) {
        // 用户B收到offer
        console.log('收到offer', offer)
        // 用户B也需要创建RTCPeerConnection，添加本地音视频流，设置远端描述信息，生成answer,并且通关信令服务器将answer发给用户A
        peer.value = new RTCPeerConnection()
        const stream = await getLocalStream()
        // 添加本地音视频流
        peer.value.addStream(stream)

        //  通过监听onicecandidate事件获取用户Bcandidate信息(网络信息)
        peer.value.onicecandidate = (event: any) => {
          console.log('用户B的candidate信息', event.candidate)
          if (event.candidate) {
            sock.emit('sendCandidate', { candidate: event.candidate, roomId })
          }
        }

        // 监听onaddstream事件来获取对方的音视频流
        peer.value.onaddstream = (event: any) => {
          console.log('用户B收到用户A的stream', event.stream)
          communicating.value = true
          calling.value = false
          remoteVideo.value!.srcObject = event.stream
          remoteVideo.value!.play()
        }

        // 设置远端描述信息 SDP
        await peer.value.setRemoteDescription(offer)
        const answer = await peer.value.createAnswer()
        console.log('用户B生成answer', answer)
        // 在本地设置answer信息
        await peer.value.setLocalDescription(answer)
        // 发送answer
        sock.emit('sendAnswer', { answer, roomId })
      }
    })

    // 用户A接收到sendAnswer
    sock.on('sendAnswer', async (answer) => {
      if (caller.value) {
        console.log(answer)
        // 设置远端answer信息
        await peer.value.setRemoteDescription(answer)
      }
    })

    // 8.用户A和用户B收到对方的candidate信息，添加到本地
    sock.on('sendCandidate', async (candidate) => {
      await peer.value.addIceCandidate(candidate)
    })

    socket.value = sock
  })

  /*********** methods **************/
  // 发起方发起视频请求
  const callRemote = async () => {
    console.log('发起视频')
    // 用户A向用户B发起视频请求
    caller.value = true // 表示当前用户是发起方
    calling.value = true
    await getLocalStream()
    // 通过信令服务器向用户B（接收方）发起视频请求
    socket.value?.emit('callRemote', roomId)
  }

  // 接收方同意视频请求
  const acceptCall = () => {
    console.log('同意视频要求')
    // 通过信令服务器通知用户A（发起方）同意视频
    socket.value?.emit('acceptCall', roomId)
  }

  // 挂断视频
  const hangUp = () => {
    console.log('挂断视频')
  }

  // 获取本地音视频流
  const getLocalStream = async () => {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true,
    })

    localVideo.value!.srcObject = stream
    localVideo.value!.play()

    localStream.value = stream

    return stream
  }
</script>
